package main
import (
	"fmt"
	"sync"
	"time"
)

//程序运行完成时一定要有输出语句，本工具才能正确展示运行结果。
//fmt.Println("Hello JSRUN!   \n\n         - from Golang .")
func main () {

	c := make(chan int)
	defer close(c)
	defer forRange(c)
	for i := 0; i < 100; i++ {
		c <- i
	}

	fmt.Println("\nHello JSRUN!   \n\n         - from Golang .")
}




/*  ---------------------
	Golang并发的次优选择：sync包
	https://mp.weixin.qq.com/s/UpYbmFTowjCPU83W3DxP6Q
*/


/*  ---------------------
	Golang并发模型：并发协程的优雅退出
	https://mp.weixin.qq.com/s/RjomKnfwCTy7tC9gbpPxCQ
*/
func forRange(inCh <-chan int) <-chan int {
	finish := make(chan int)
	go func (in <-chan int)  {
	 	// Using for-range to exit goroutine
	    // range has the ability to detect the close/end of a channel
		for x := range in {
			fmt.Printf("Process %d\n", x)
		}	
	}(inCh)
	return finish
}

/*  ---------------------
	Golang并发模型：轻松入门流水线FAN模式
*/
func testPS_FAN() {
	t1 := time.Now()
	for i := 1; i < 10000; i++ {
		in := producer(i)
		ch1 := square(in)    
		ch2 := square(in) 
		ch3 := square(in) 

		for range merge(ch1,ch2,ch3) { 
			//fmt.Printf("%d  ", ret)    ret := 
		}
	}
	elapsed1 := time.Since(t1)
	fmt.Println("\n testPS_FAN elapsed: ", elapsed1)

	fmt.Println()
}
func merge(cs ...<-chan int) <-chan int {
	var wg sync.WaitGroup
	out := make(chan int)

	collect := func(in <-chan int){
		defer wg.Done()
		for n := range in {
			out <- n
		}
	}

	wg.Add(len(cs))
	
	for _, c := range cs {
		go 	collect(c)
	}
	
	go func(){
		wg.Wait()
		close(out)
	}()
	return out
}

/*  ---------------------
	Golang并发模型：轻松入门流水线模型
*/
func testPS() {
	t1 := time.Now()
	for i := 1; i < 10000; i++ {
		in := producer(i)
		ch := square(in)    
		for range ch {
			//fmt.Printf("%d  ", ret)  ret :=     
		}
	}
	elapsed1 := time.Since(t1)
	fmt.Println("\n testPS elapsed: ", elapsed1)

	fmt.Println()
}

func producer(nums ...int) <-chan int {
	out := make(chan int)
	go func(){
		defer close(out)
		for _, num := range nums {
			out <- num
		}
	}()
	return out
}
func square(intCh <-chan int) <-chan int {
	out := make(chan int)
	go func(){
		defer close(out)
		for num := range intCh {
			out <- num * num
		}
	}()
	return out
}



/*  ---------------------
	chan使用过程关于close()
	close函数是一个内建函数， 用来关闭channel，这个channel要么是双向的， 要么是只写的（chan<- Type）。
	这个方法应该只由发送者调用， 而不是接收者。
*/
func testWrite_Read(){
	c1 := make(chan int)
	chWait := make(chan bool)

    go write(c1)
	go read(c1,chWait)
	<- chWait
}
func write(ch chan int){
	fmt.Println("write .")
	for i:=0; i<5; i++ {
		ch <- i
	}
	close(ch)
}

func read(c1 chan int ,cw chan bool){
	fmt.Println("read .")
	for{
		if c,ok :=  <- c1; ok {
			fmt.Printf("read a int is %d\n",c)
		}else{
			cw <- true
		}
	}
	
}


/*  ---------------------
	//主线程等待子线程都运行完再退出 1
*/
func syncWait() {
	
	var wg sync.WaitGroup
	wg.Add(2)
	go func(){ 
		fmt.Println("Goroutine 1")
		wg.Done()
	}()
	go func(){ 
		fmt.Println("Goroutine 2")
		wg.Done()
	}()
	wg.Wait()
}
/*  ---------------------
	//主线程等待子线程都运行完再退出 2
*/
func chanWait(){
	ch := make(chan struct{})
	count := 2
	go func(){ 
		fmt.Println("chanWait Goroutine 1")
		ch <- struct{}{}
	}()
	go func(){ 
		fmt.Println("chanWait Goroutine 2")
		ch <- struct{}{}
	}()

	for range ch {
		count--
		if count == 0 {
			close(ch)
		}
	}
}


/*  ---------------------
	Register_Test()
*/
var p = make(map[string]int)
func Register(name string,num int){
    if _,dup := p[name]; dup {
          fmt.Println("logs: Register called twice for provider " + name)
    }
    p[name] = num
}

func Register_Test(){
    Register("v1",1)
    Register("v1",1)
    Register("v2",2)
    fmt.Println(p["v1"])
    fmt.Println(p["v2"])
}
